<template>
  <div class="color-slider-container">
    <div
      class="color-slider-inner"
      ref="targetRef"
      style="
        background: linear-gradient(
          90deg,
          rgb(255, 0, 0) 0%,
          rgb(255, 255, 0) 17%,
          rgb(0, 255, 0) 33%,
          rgb(0, 255, 255) 50%,
          rgb(0, 0, 255) 67%,
          rgb(255, 0, 255) 83%,
          rgb(255, 0, 0) 100%
        );
      "
      @click="(e) => onUpdateBounds(e.offsetX)"
    ></div>
    <div
      ref="dotRef"
      class="color-slider-handle"
      :style="{ '--color': value }"
      v-drag:horizontal="{ onMoveChange }"
    ></div>
  </div>
</template>

<script setup>
import { onMounted, ref, toValue, watch } from 'vue';
import { formatRgbColor, hueToRgbArray, rgbToHex, rgbToHue } from '@/utils/color/index.js';

defineOptions({
  inheritAttrs: false
});

const props = defineProps({
  defaultValue: { type: String, default: 'rgb(0, 255, 255)' },
  format: { type: String, default: 'rgb' }
});

const emit = defineEmits(['update:value', 'change']);
const value = defineModel('value', { type: String, default: '' });
const targetRef = ref(null);
const dotRef = ref(null);

onMounted(() => {
  setTimeout(() => initValue(), 100);
});

watch(value, () => {
  initValue();
});

/**
 * 更新值
 * @param value
 */
const updateValue = (value) => {
  if (props.format === 'hex') {
    value = rgbToHex(value);
  }
  emit('change', value);
  emit('update:value', value);
};

/**
 * 初始化值
 */
function initValue() {
  // 获取宽度
  const { offsetWidth } = toValue(targetRef);
  const rgb = formatRgbColor(toValue(value) || toValue(props.defaultValue));
  // 获取色相
  const hue = rgbToHue(rgb);
  // 设置距离
  const left = Math.round((hue / 360) * offsetWidth);
  onUpdateBounds(left);
}

/**
 * 更新位置
 * @param left 横坐标
 */
const onUpdateBounds = (left) => {
  toValue(dotRef).updateBounds({ left });
};

/**
 * 移动变化
 * @param left 横坐标
 * @param maxLeft 宽度
 */
const onMoveChange = ({ left, maxLeft }) => {
  // 计算色相;
  const val = left / toValue(maxLeft);
  const hue = Math.round((val * 360 * 100) / 100);
  // 转换为rgb
  const rgbArray = hueToRgbArray(hue);
  const rgb = `rgb(${rgbArray.join(', ')})`;
  toValue(value) === rgb || updateValue(rgb);
};
</script>

<style lang="scss" scoped>
.color-slider-container {
  width: 280px;
  padding: 8px 0;
  display: flex;
  position: relative;
  user-select: none;

  .color-slider-inner {
    width: 100%;
    height: 6px;
    border-radius: 6px;
    cursor: pointer;
  }

  .color-slider-handle {
    --width: 12px;
    cursor: pointer;
    left: var(--left);
    top: calc(50% - (var(--width) / 2));
    width: var(--width);
    height: var(--width);
    border-radius: 50%;
    z-index: 1;
    position: absolute;
    background-color: var(--color);
    transform: translate(-50%, 0);

    &::before {
      --border-width: 4px;
      content: '';
      display: block;
      width: calc(var(--width) + var(--border-width));
      height: calc(var(--width) + var(--border-width));
      border-radius: 50%;
      background-color: transparent;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      border: 2px solid #ffffff;
      box-shadow:
        inset 0 0 4px var(--box-shadow-color),
        0 0 4px var(--box-shadow-color);
    }
  }
}
</style>
